
 <!DOCTYPE HTML>
<html lang="zh-CN">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
  
    <title>使用 flying 解决 pojo 自动映射问题 | my bat is flying in Aurora</title>
    <meta name="viewport" content="width=device-width, initial-scale=1,user-scalable=no">
    
    <meta name="author" content="limeng32">
    

    
    <meta name="description" content="本节内容向您讲解如何使用 AutoMapperInterceptor 拦截器来实现pojo的自动映射。">
<meta property="og:type" content="article">
<meta property="og:title" content="使用 flying 解决 pojo 自动映射问题">
<meta property="og:url" content="http://flying-doc.limeng32.com/2017/05/01/2017-05-01-使用flying解决pojo自动映射问题/index.html">
<meta property="og:site_name" content="my bat is flying in Aurora">
<meta property="og:description" content="本节内容向您讲解如何使用 AutoMapperInterceptor 拦截器来实现pojo的自动映射。">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2021-06-06T06:23:40.468Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="使用 flying 解决 pojo 自动映射问题">
<meta name="twitter:description" content="本节内容向您讲解如何使用 AutoMapperInterceptor 拦截器来实现pojo的自动映射。">

    
    <link rel="alternative" href="/atom.xml" title="my bat is flying in Aurora" type="application/atom+xml">
    
    
    
    <link rel="stylesheet" href="/css/style.css">
</head>
</html>
  <body>
    <header>
      
<div>
		
			<div id="textlogo">
				<h1 class="site-name"><a href="/" title="my bat is flying in Aurora">my bat is flying in Aurora</a></h1>
				<h2 class="blog-motto"></h2>
			</div>
			<div class="navbar"><a class="navbutton navmobile" href="#" title="菜单">
			</a></div>
			<nav class="animated">
				<ul>
					<ul>
					 
						<li><a href="/">Home</a></li>
					
						<li><a href="/archives">Archives</a></li>
					
						<li><a href="/about">About</a></li>
					
					<li>
 					
					<form class="search" action="//google.com/search" method="get" accept-charset="utf-8">
						<label>Search</label>
						<input type="search" id="search" name="q" autocomplete="off" maxlength="20" placeholder="搜索" />
						<input type="hidden" name="q" value="site:flying-doc.limeng32.com">
					</form>
					
					</li>
				</ul>
			</nav>			
</div>
    </header>
    <div id="container">
      <div id="main" class="post" itemscope itemprop="blogPost">
  
	<article itemprop="articleBody"> 
		<header class="article-info clearfix">
  <h1 itemprop="name">
    
      <a href="/2017/05/01/2017-05-01-使用flying解决pojo自动映射问题/" title="使用 flying 解决 pojo 自动映射问题" itemprop="url">使用 flying 解决 pojo 自动映射问题</a>
  </h1>
  <p class="article-author">By
       
		<a href="/about" title="limeng32" target="_blank" itemprop="author">limeng32</a>
		
  <p class="article-time">
    <time datetime="2017-04-30T16:00:00.000Z" itemprop="datePublished"> 发表于 2017-05-01</time>
    
  </p>
</header>
	<div class="article-content">
		
		<div id="toc" class="toc-article">
			<strong class="toc-title">文章目录</strong>
		
		<ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Hello-World"><span class="toc-text">Hello World</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#flying-json-描述"><span class="toc-text">flying-json 描述</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#insert-amp-delete"><span class="toc-text">insert &amp; delete</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#update-amp-updatePersistent"><span class="toc-text">update &amp; updatePersistent</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#selectAll-amp-selectOne-amp-count"><span class="toc-text">selectAll &amp; selectOne &amp; count</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#foreign-key"><span class="toc-text">foreign key</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#复杂外键关系"><span class="toc-text">复杂外键关系</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#complex-condition"><span class="toc-text">complex condition</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#limiter-amp-sorter"><span class="toc-text">limiter &amp; sorter</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#分页"><span class="toc-text">分页</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#乐观锁"><span class="toc-text">乐观锁</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#或逻辑查询"><span class="toc-text">或逻辑查询</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#外键或逻辑查询"><span class="toc-text">外键或逻辑查询</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#customTypeHandler"><span class="toc-text">customTypeHandler</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#其它"><span class="toc-text">其它</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#ignore-tag"><span class="toc-text">ignore tag</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#whiteList-tag"><span class="toc-text">whiteList tag</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#复数外键"><span class="toc-text">复数外键</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#兼容-JPA-标签"><span class="toc-text">兼容 JPA 标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#附录"><span class="toc-text">附录</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#常见问题"><span class="toc-text">常见问题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#代码示例"><span class="toc-text">代码示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#account-表建表语句"><span class="toc-text">account 表建表语句</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#role-表建表语句"><span class="toc-text">role 表建表语句</span></a></li></ol></li></ol>
		
		</div>
		
		<h2 id="Hello-World"><a href="#Hello-World" class="headerlink" title="Hello World"></a><a href="#Hello-World">Hello World</a></h2><p>上一篇文章中我们介绍了 flying 的基本情况，在展示第一个 demo 之前还需要做一些额外的工作，即描述您想让 mybatis 管理的数据的表结构。</p>
<p>无论是否使用 flying 插件，对于每一个由 mybatis 托管的表，都要有一个 <i>pojo_mapper</i>.xml 来告诉 mybatis 这个表的基本信息。在以往这个配置文件可能会因为 sql 片段而变得非常复杂，但加入 flying 插件后，这个配置文件中将不需要 sql 片段，变得精简而统一。下面是 <a href="#AccountTableCreater">一个有代表性的 account 表</a> 以及对应它的配置文件 account.xml ：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br><span class="line"><span class="meta">&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"myPackage.AccountMapper"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">cache</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"select"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">        &#123;"action":"select#&#123;?&#125;"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectOne"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">        &#123;"action":"selectOne"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">"result"</span> <span class="attr">type</span>=<span class="string">"Account"</span> <span class="attr">autoMapping</span>=<span class="string">"true"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">"id"</span> <span class="attr">column</span>=<span class="string">"account_id"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>在以上配置文件中，我们描述了一个接口 myPackage.AccountMapper，一个方法 select ，一个方法 selectOne，一个对象实体 Account，以及数据库表结构 resultMap。在 resultMap 中由于设置了 <code>autoMapping=&quot;true&quot;</code>，我们只需要写出主键（以及外键，在稍后的章节会讲到），mybatis 会自动感知与 Account.java 中对应的变量名相同的字段，但与变量名有差异的字段仍需在 resultMap 中声明。</p>
<p>myPackage.AccountMapper 接口是 mybatis 本身需要的，里面的内容和 account.xml 中定义的方法相对应。如果您有使用 mybatis 的经验您就能立刻想到， AccountMapper.java 中的内容是：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">AccountMapper</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Account <span class="title">selectOne</span><span class="params">(Account t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>到目前为止一切都和不使用 flying 时一模一样，您可能奇怪的地方是：account.xml 中的 select 和 selectOne 方法描述中的 json 是什么。这是这条查询的 flying-json，flying 将以往的 sql 语句抽离为 json 和执行对象，<a href="#flying-json-描述">在 flying-json 描述部分会有解释。</a>马上我们就会在对象实体 Account 中看到更多不一样的地方，Account.java 的代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.type.JdbcType;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.FieldMapperAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.TableMapperAnnotation;</span><br><span class="line"></span><br><span class="line"><span class="meta">@TableMapperAnnotation</span>(tableName = <span class="string">"account"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"account_id"</span>, jdbcType = JdbcType.INTEGER, isUniqueKey = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, jdbcType = JdbcType.VARCHAR)</span><br><span class="line">    <span class="keyword">private</span> java.lang.String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Integer <span class="title">getId</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setId</span><span class="params">(Integer id)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.id = id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">      <span class="keyword">return</span> name;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">      <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>可见，和普通的 pojo 相比， Account.java 只是多了以下3行注解而已：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@TableMapperAnnotation</span>(tableName = <span class="string">"account"</span>)</span><br><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"id"</span>, jdbcType = JdbcType.INTEGER, isUniqueKey = <span class="keyword">true</span>)</span><br><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, jdbcType = JdbcType.VARCHAR)</span><br></pre></td></tr></table></figure></p>
<p>下面我们分别来解释它们的含义。</p>
<p>第1行 <code>@TableMapperAnnotation</code> 只能放在类定义之上，它声明这个类是一个表，它的属性 <code>tableName</code> 描述了这个表在数据库中的名字。</p>
<p>第2行 <code>@FieldMapperAnnotation</code> 只能放在变量定义之上，它声明这个变量是一个字段，它的属性 <code>dbFieldName</code> 描述了在数据库中这个字段的名称，它的属性 <code>jdbcType</code> 描述了在数据库中这个字段的类型，它的属性 <code>isUniqueKey = true</code> 描述了这个字段是一个主键。</p>
<p>第3行 <code>@FieldMapperAnnotation</code> 与第二行相同，它描述了另一个字段 name，值得注意的是这个字段的类型是 varchar 并且不是主键。</p>
<p>以上 3 个注解描述了表 account 的数据结构，然后我们就可以使用 AccountMapper 非常方便的操纵数据库的读取了。</p>
<p>使用以下代码，可以查询 id 为 1 的账户：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Account account = accountMapper.select(<span class="number">1</span>);</span><br></pre></td></tr></table></figure></p>
<p>使用以下代码，可以查询 name 为 andy 的 1 条账户数据：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Account accountCondition = <span class="keyword">new</span> Account();</span><br><span class="line">accountCondition.setName(<span class="string">"andy"</span>);</span><br><span class="line">Account account = accountMapper.selectOne(accountCondition);</span><br></pre></td></tr></table></figure></p>
<p>与以往的方式相比，这种方式是不是变得优雅了很多？关于 select 和 selectOne 之间的区别，我们在后面的章节会讲到。</p>
<h2 id="flying-json-描述"><a href="#flying-json-描述" class="headerlink" title="flying-json 描述"></a><a href="#flying-json-描述">flying-json 描述</a></h2><p><i>pojo_mapper</i>.xml 中的 {“action”:”select#{?}”} 即是 flying 的特征描述，如果您想用 flying 管理一个数据库操作，就用这样一个 json 替代原本应该写的 sql 语句，它的格式使用 linux 风格描述如下（由 __ 开头和结尾的参数需由用户提供）：</p>
<p>查询操作：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "action":"select#&#123;?&#125;|selectAll|selectOne"</span><br><span class="line">    [, "ignore":"__ignore__"][, "whiteList":"__whiteList__"]</span><br><span class="line">    [, "index":"__index__"]</span><br><span class="line">    [, "properties":&#123;</span><br><span class="line">      "__property_1__":&#123;"id":"__id_1__", "prefix":"__prefix_1__"&#125;[ ,...n ]</span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>查询数量操作：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "action":"count"[, "index":"__index__"]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>修改操作：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "action":"update|updatePersistent|updateBatch"</span><br><span class="line">    [, "ignore":"__ignore__"][, "whiteList":"__whiteList__"]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>删除操作：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "action":"delete"</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>新增操作：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  "action":"insert|insertBatch"</span><br><span class="line">    [, "ignore":"__ignore__"][, "whiteList":"__whiteList__"]</span><br><span class="line">    [, "keyGenerator":"uuid|uuid_no_line|millisecond|__your.custom.KeyHandler__"]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>为避免特定情况下的缓存问题，当您使用 select 操作时需在它后面加上 #{?} 变成 select#{?}，当您使用其它类型操作时不需要这么做。</p>
<p>“action” 参数是 flying 操作数据的方法，目前支持的方法有：<br><code>select#{?}</code>：按主键查询，并返回结果集中的对象；<br><code>selectOne</code>：按条件对象查询，只返回结果集中的第一个对象；<br><code>selectAll</code>：按条件对象查询，返回结果集中所有对象组成的集合；<br><code>count</code>：按条件对象查询，返回结果数量；<br><code>update</code>：按参数对象中的非 null 属性更新记录，可按主键执行一条也可批量执行多条；<br><code>updatePersistent</code>：按参数对象中的所有属性更新一条记录，以参数主键为准，此操作会把参数对象为 null 的属性在数据库中也更新为 [null]；<br><code>updateBatch</code>：按集合型参数对象同时更新多条记录，更新方式同 <code>update</code>（<code>1.0.1</code> 版新增）；<br><code>delete</code>：按参数对象删除记录，可按主键执行一条也可批量执行多条；<br><code>insert</code>：按参数对象增加一条记录，在 insert 之后可以以括号的方式指定主键生成方式（可选），内置有 uuid、无下横线的 uuid_no_line、按毫秒值 millisecond，也可完全自定义主键生成器类；<br><code>insertBatch</code>：按集合型参数对象同时增加多条记录，”keyGenerator” 同 <code>insert</code>（<code>1.0.0</code> 版新增）；</p>
<p>本文为描述方便，大部分方法名（即方法配置中的 id）与其操作类型（即 json 中的 “action”）相同，实际上方法名可以任意取，当您打算在同一个 <i>pojo_mapper</i>.xml 中定义多个操作类型相同的方法时就会发现这一点。如果您有更多操作类型的想法请告诉我们。</p>
<p>“ignore” 参数内容是黑名单标记，更多的内容请见 <a href="#ignore-tag">本文 ignore tag 部分。</a> “whiteList” 参数内容是白名单标记，更多的内容请见 <a href="#whiteList-tag">本文 whiteList tag 部分。</a></p>
<p>“index” 参数内容为指定索引语句，更多内容请见后。</p>
<p>“properties” 参数内容为外键关联表的查询情况，这里使用 <i>pojo_mapper</i>.xml 中的 resultMap 中的 association 中的 columnPrefix 机制，使得我们可在一次查询中获得外键关联表中所有需要的记录，这是处理关联查询的最佳方式，更多内容请见 <a href="#foreign-key">本文 foreign key 部分。</a>。</p>
<h2 id="insert-amp-delete"><a href="#insert-amp-delete" class="headerlink" title="insert &amp; delete"></a><a href="#insert-amp-delete">insert &amp; delete</a></h2><p>在最基本的 select 之后，我们再看新增功能。但在此之前，需要先在 account.xml 中增加以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insert"</span> <span class="attr">useGeneratedKeys</span>=<span class="string">"true"</span> <span class="attr">keyProperty</span>=<span class="string">"id"</span>&gt;</span></span><br><span class="line">    &#123;"action":"insert"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>上面的 <code>useGeneratedKeys=&quot;true&quot;</code> 表示主键自增，如果您不使用主键自增策略此处可以省略，上面的语句和一般 mybatis 映射文件的区别在于具体 sql 语句变成了 json。</p>
<p>同样在 AccountMapper.java 中我们需要加入：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Account t)</span></span>;</span><br></pre></td></tr></table></figure></p>
<p>然后使用以下代码，可以增加 1 条 name 为 “bob” 的账户数据（由于我们配置了主键自增，新增数据时不需要指定主键）：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Account newAccount = <span class="keyword">new</span> Account();</span><br><span class="line">newAccount.setName(<span class="string">"bob"</span>);</span><br><span class="line">accountMapper.insert(newAccount);</span><br></pre></td></tr></table></figure></p>
<p>然后我们再看删除功能。先在 account.xml 中增加以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">"delete"</span>&gt;</span></span><br><span class="line">    &#123;"action":"delete"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>然后在 <code>AccountMapper.java</code> 中加入：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Account t)</span></span>;</span><br></pre></td></tr></table></figure></p>
<p>然后使用以下代码，可以删掉 id 与 accountToDelete 的 id 一致的数据。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">accountMapper.delete(accountToDelete);</span><br></pre></td></tr></table></figure></p>
<p>delete 方法的返回值代表执行 sql 后产生影响的条数，一般来说，返回值为 0 表示 sql 执行后没有效果，返回值为 1 表示 sql 执行成功，在代码中可以通过判断 delete 方法的返回值来实现更复杂的事务逻辑。</p>
<p>delete 操作支持批量执行，这点在进阶部分我们会进行讨论。</p>
<h2 id="update-amp-updatePersistent"><a href="#update-amp-updatePersistent" class="headerlink" title="update &amp; updatePersistent"></a><a href="#update-amp-updatePersistent">update &amp; updatePersistent</a></h2><p>接下来我们看看更新功能，这里我们要介绍两个方法：update（更新）和 updatePersistent（完全更新）。首先，在 <code>account.xml</code> 中增加以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"update"</span>&gt;</span></span><br><span class="line">    &#123;"action":"update"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"updatePersistent"</span>&gt;</span></span><br><span class="line">    &#123;"action":"updatePersistent"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>上面的语句和一般 mybatis 映射文件的区别在于具体 sql 语句变成了 json。</p>
<p>然后在 <code>AccountMapper.java</code> 中加入：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Account t)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Account t)</span></span>;</span><br></pre></td></tr></table></figure></p>
<p>然后使用以下代码，可以将 accountToUpdate 的 name 更新为 “duke” 。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">accountToUpdate.setName(<span class="string">"duke"</span>);</span><br><span class="line">accountMapper.update(accountToUpdate);</span><br></pre></td></tr></table></figure></p>
<p>update 和 updatePersistent 方法的返回值代表执行 sql 后产生影响的条数，一般来说，返回值为 0 表示 sql 执行后没有效果，返回值为 1 表示 sql 执行成功，在代码中可以通过判断 update 和 updatePersistent 方法的返回值来实现更复杂的事务逻辑。</p>
<p>下面我们来说明 update 和 updatePersistent 和关系。如果我们执行<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">accountToUpdate.setName(<span class="keyword">null</span>);</span><br><span class="line">accountMapper.update(accountToUpdate);</span><br></pre></td></tr></table></figure></p>
<p>实际上数据库中这条数据的 name 字段不会改变，因为 update 对值为 null 的属性有保护措施。这在大多数情况下都是合理的，但如果我们真的需要在数据库中将这条数据的 name 字段设为 null，updatePersistent 就派上了用场。我们可以执行：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">accountToUpdate.setName(<span class="keyword">null</span>);</span><br><span class="line">accountMapper.updatePersistent(accountToUpdate);</span><br></pre></td></tr></table></figure></p>
<p>这样数据库中这条数据的 name 字段就会变为 null。可见 updatePersistent 会把 pojo 中所有的属性都更新到数据库中，而 update 只更新不为 null 的属性。在实际使用 updatePersistent 时，您需要特别小心慎重，因为当时 pojo 中为 null 的属性有可能比您想象的多。</p>
<p>update 操作支持批量执行，这点在进阶部分我们会进行讨论。</p>
<h2 id="selectAll-amp-selectOne-amp-count"><a href="#selectAll-amp-selectOne-amp-count" class="headerlink" title="selectAll &amp; selectOne &amp; count"></a><a href="#selectAll-amp-selectOne-amp-count">selectAll &amp; selectOne &amp; count</a></h2><p>在之前学习 select 和 selectOne 时，细心的您可能已经发现，这两个方法要完成的工作似乎是相同的。的确 select 和 selectOne 都返回 1 个绑定了数据的 pojo，但它们接受的参数不同：select 接受主键参数；selectOne 接受 pojo 参数，这个 pojo 中的所有被 <code>@FieldMapperAnnotation</code> 标记过的属性都会用 “等于” 条件传递到 sql 语句中。之所以要这么设计，是因为我们有时会需要按照一组条件返回多条数据或者数量，即 selectAll 方法与 count 方法，这个时候以 pojo 作为入参最为合适。为了更清晰的讲述，我们先给 <code>Account.java</code> 再增加一个属性 address：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, jdbcType = JdbcType.VARCHAR)</span><br><span class="line"><span class="keyword">private</span> java.lang.String address;</span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>然后我们在 <code>account.xml</code> 中增加以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectAll"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">    &#123;"action":"selectAll"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"count"</span> <span class="attr">resultType</span>=<span class="string">"int"</span>&gt;</span></span><br><span class="line">    &#123;"action":"count"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>再在 <code>AccountMapper.java</code> 中加入<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Collection&lt;Account&gt; <span class="title">selectAll</span><span class="params">(Account t)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Account t)</span></span>;</span><br></pre></td></tr></table></figure></p>
<p>就可以了。例如使用以下代码，可以查询所有 address 为 “beijing” 的数据和数量：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Account condition = <span class="keyword">new</span> Account();</span><br><span class="line">condition.setAddress(<span class="string">"beijing"</span>);</span><br><span class="line">Collection&lt;Account&gt; accountCollection = accountMapper.selectAll(condition);</span><br><span class="line"><span class="keyword">int</span> accountNumber = accountMapper.count(condition);</span><br></pre></td></tr></table></figure></p>
<p>（当然一般来说执行 selectAll 后就不需要执行 count 了，我们取结果集的 size() 即可，但如果我们只关心数量不关心具体数据集时，执行 count 比执行 selectAll 更节省时间）</p>
<p>如果我们想查询所有 address 为 “shanghai” 同时 name 为 “ella” 的账户，则执行以下代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Account condition = <span class="keyword">new</span> Account();</span><br><span class="line">condition.setAddress(<span class="string">"shanghai"</span>);</span><br><span class="line">condition.setName(<span class="string">"ella"</span>);</span><br><span class="line">Collection&lt;Account&gt; accountCollection = accountMapper.selectAll(condition);</span><br></pre></td></tr></table></figure></p>
<p>如果我们知道 address 为 “shanghai” 同时  name 为 “ella” 的账户只有一个，并想直接返回这个数据绑定的 pojo，可以执行：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Account account = accountMapper.selectOne(condition);</span><br></pre></td></tr></table></figure></p>
<p>由此可见 selectOne 可以称作是 selectAll 的特殊形式，它只会返回一个 pojo 而不是 pojo 的集合。如果真的有多条数据符合给定的 codition ，也只会返回查询结果中排在最前面的数据。尽管如此，在合适的地方使用 selectOne 代替 selectAll，会让您的程序获得极大方便。</p>
<h2 id="foreign-key"><a href="#foreign-key" class="headerlink" title="foreign key"></a><a href="#foreign-key">foreign key</a></h2><p>一般来说我们的 pojo 都是业务相关的，而这些相关性归纳起来无外乎一对一、一对多和多对多。其中一对一是一对多的特殊形式，多对多本质上是由两个一对多组成，所以我们只需要着重解决一对多关系就可以，而 flying 就是为此而生的。</p>
<p>首先我们定义一个新的 pojo：角色（role）。角色和账户是一对多关系，即一个账户只能拥有一个角色，一个角色可以被多个账户拥有。为此我们要新建 <a href="#RoleTableCreater">一个有代表性的 role 表</a>、<code>role.xml</code>、<code>RoleMapper.java</code> 以及 <code>Role.java</code>。<code>role.xml</code> 如下：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br><span class="line"><span class="meta">&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"myPackage.RoleMapper"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">cache</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"select"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">        &#123;"action":"select#&#123;?&#125;"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectOne"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">        &#123;"action":"selectOne"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectAll"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">        &#123;"action":"selectAll"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"count"</span> <span class="attr">resultType</span>=<span class="string">"int"</span>&gt;</span></span><br><span class="line">        &#123;"action":"count"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insert"</span> <span class="attr">useGeneratedKeys</span>=<span class="string">"true"</span> <span class="attr">keyProperty</span>=<span class="string">"id"</span>&gt;</span></span><br><span class="line">        &#123;"action":"insert"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"update"</span>&gt;</span></span><br><span class="line">        &#123;"action":"update"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"updatePersistent"</span>&gt;</span></span><br><span class="line">        &#123;"action":"updatePersistent"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">"delete"</span>&gt;</span></span><br><span class="line">        &#123;"action":"delete"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">"result"</span> <span class="attr">type</span>=<span class="string">"Role"</span> <span class="attr">autoMapping</span>=<span class="string">"true"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">"id"</span> <span class="attr">column</span>=<span class="string">"role_id"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p><code>RoleMapper.java</code> 如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">RoleMapper</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Role <span class="title">select</span><span class="params">(Object id)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Role <span class="title">selectOne</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> Collection&lt;Role&gt; <span class="title">selectAll</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">insert</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">update</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">updatePersistent</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">delete</span><span class="params">(Role t)</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">count</span><span class="params">(Role t)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p><code>Role.java</code> 如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> org.apache.ibatis.type.JdbcType;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.FieldMapperAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.TableMapperAnnotation;</span><br><span class="line"></span><br><span class="line"><span class="meta">@TableMapperAnnotation</span>(tableName = <span class="string">"role"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Role</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"role_id"</span>, jdbcType = JdbcType.INTEGER, isUniqueKey = <span class="keyword">true</span>)</span><br><span class="line">    <span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"role_name"</span>, jdbcType = JdbcType.VARCHAR)</span><br><span class="line">    <span class="keyword">private</span> String roleName;</span><br><span class="line">    <span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>然后在 <code>Account.java</code> 中，加入以下内容：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"fk_role_id"</span>, jdbcType = JdbcType.INTEGER, dbAssociationUniqueKey = <span class="string">"role_id"</span>)</span><br><span class="line"><span class="keyword">private</span> Role role;   </span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>以上代码中，<code>dbFieldName</code> 的值为数据库表 account 中指向表 role 的外键名，<code>jdbcType</code> 的值为这个外键的类型，<code>dbAssociationUniqueKey</code> 的值为此外键对应的另一表的主键的名称，写出以上信息后，flying 在代码层面已经完全理解了数据结构。</p>
<p>然后后在 <code>account.xml</code> 的 <code>resultMap</code> 元素中，加入以下内容<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">association</span> <span class="attr">property</span>=<span class="string">"role"</span> <span class="attr">resultMap</span>=<span class="string">"myPackage.RoleMapper.result"</span> <span class="attr">columnPrefix</span>=<span class="string">"role__"</span> /&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>同时给 <code>account.xml</code> 的 <code>select</code>、<code>selectAll</code>、<code>selectOne</code> 的 json 中加入以下内容<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;properties&quot;:&#123;&quot;role&quot;:&#123;&quot;id&quot;:&quot;myPackage.RoleMapper.select&quot;, &quot;prefix&quot;:&quot;role__&quot;&#125;&#125;</span><br></pre></td></tr></table></figure></p>
<p>写出以上信息后，flying 在配置文件层面已经完全理解了数据结构。</p>
<p>最后总结一下，完整版的 <code>account.xml</code> 如下：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span><br><span class="line"><span class="meta">&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">mapper</span> <span class="attr">namespace</span>=<span class="string">"myPackage.AccountMapper"</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">cache</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"select"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">      &#123;"action":"select#&#123;?&#125;", "properties":&#123;</span><br><span class="line">        "role":&#123;"id":"myPackage.RoleMapper.select", "prefix":"role__"&#125;,</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectOne"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">      &#123;"action":"selectOne", "properties":&#123;</span><br><span class="line">        "role":&#123;"id":"myPackage.RoleMapper.select", "prefix":"role__"&#125;,</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectAll"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">      &#123;"action":"selectAll", "properties":&#123;</span><br><span class="line">        "role":&#123;"id":"myPackage.RoleMapper.select", "prefix":"role__"&#125;,</span><br><span class="line">      &#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"count"</span> <span class="attr">resultType</span>=<span class="string">"int"</span>&gt;</span></span><br><span class="line">        &#123;"action":"count"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">insert</span> <span class="attr">id</span>=<span class="string">"insert"</span> <span class="attr">useGeneratedKeys</span>=<span class="string">"true"</span> <span class="attr">keyProperty</span>=<span class="string">"id"</span>&gt;</span></span><br><span class="line">        &#123;"action":"insert"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">insert</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"update"</span>&gt;</span></span><br><span class="line">        &#123;"action":"update"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">update</span> <span class="attr">id</span>=<span class="string">"updatePersistent"</span>&gt;</span></span><br><span class="line">        &#123;"action":"updatePersistent"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">update</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">delete</span> <span class="attr">id</span>=<span class="string">"delete"</span>&gt;</span></span><br><span class="line">        &#123;"action":"delete"&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">delete</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">resultMap</span> <span class="attr">id</span>=<span class="string">"result"</span> <span class="attr">type</span>=<span class="string">"Account"</span> <span class="attr">autoMapping</span>=<span class="string">"true"</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span> <span class="attr">property</span>=<span class="string">"id"</span> <span class="attr">column</span>=<span class="string">"account_id"</span> /&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">association</span> <span class="attr">property</span>=<span class="string">"role"</span> <span class="attr">resultMap</span>=<span class="string">"myPackage.RoleMapper.result"</span> <span class="attr">columnPrefix</span>=<span class="string">"role__"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">resultMap</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mapper</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>上述内容中 json 中的 <code>properties</code> 中的 <code>&quot;prefix&quot;</code> 要和 <code>resultMap</code> 中的 <code>association</code> 中的 <code>&quot;columnPrefix&quot;</code> 保持一致，这样才能借助 mybatis 对外键关联表内容进行正确解析（这里前缀值 “role__” 可以换成用户自定义的任意值）。json 中的 <code>properties</code> 中的 <code>&quot;id&quot;</code> 指向了另一个方法（注意这里使用了 mybatis 内部 id 机制），说明它调用这个方法的 json 来处理此 property。这样一来我们就可以把所有有关的 resultMap 用 json 关联起来。</p>
<p>在写完以上代码后，我们看看 flying 能做到什么。首先多对一关系中的<b>一</b>（也即父对象），是可以在多对一关系中的<b>多</b>（也即子对象）查询时自动查询的。为了说明接下来的例子，我们先以 dataset 的方式定义一个数据集<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dataset</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"1"</span> <span class="attr">fk_role_id</span>=<span class="string">"10"</span> <span class="attr">address</span>=<span class="string">"beijing"</span> <span class="attr">name</span>=<span class="string">"frank"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"2"</span> <span class="attr">fk_role_id</span>=<span class="string">"11"</span> <span class="attr">address</span>=<span class="string">"tianjin"</span> <span class="attr">name</span>=<span class="string">"gale"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">account</span> <span class="attr">account_id</span>=<span class="string">"3"</span> <span class="attr">fk_role_id</span>=<span class="string">"11"</span> <span class="attr">address</span>=<span class="string">"beijing"</span> <span class="attr">name</span>=<span class="string">"hank"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">role</span> <span class="attr">role_id</span>=<span class="string">"10"</span> <span class="attr">role_name</span>=<span class="string">"user"</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">role</span> <span class="attr">role_id</span>=<span class="string">"11"</span> <span class="attr">role_name</span>=<span class="string">"super_user"</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dataset</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>我们使用这个数据集进行测试，当我们输入以下代码时：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Account account1 = accountMapper.select(<span class="number">1</span>);</span><br><span class="line"><span class="comment">/*此时account1的role属性也已经加载了真实数据*/</span></span><br><span class="line">Role role1 = account1.getRole();</span><br><span class="line"><span class="comment">/*role1.getId()为10，role1.getRoleName()为"user"*/</span></span><br></pre></td></tr></table></figure></p>
<p>这种传递是可以迭代的，即如果 Role 自己也有父对象，则 Role 的父对象也会一并加载，只要它的配置文件和代码正确。</p>
<p>不仅如此，我们可以在入参 pojo 中加入父对象，比如下面的代码查询的是角色名为 “super_user” 的所有帐户：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">Role roleCondition = <span class="keyword">new</span> Role();</span><br><span class="line">roleCondition.setRoleName(<span class="string">"super_user"</span>);</span><br><span class="line">Account accountCondition = <span class="keyword">new</span> Account();</span><br><span class="line">accountCondition.setRole(roleCondition);</span><br><span class="line">Collection&lt;Account&gt; accounts = accountMapper.selectAll(accountCondition);</span><br><span class="line"><span class="comment">/*accounts.seiz()为 2，里面包含的对象的 account_id 是 2 和 3*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*我们再给入参pojo加一个address限制*/</span></span><br><span class="line">accountCondition.setAddress(<span class="string">"beijing"</span>);</span><br><span class="line">Collection&lt;Account&gt; accounts2 = accountMapper.selectAll(accountCondition);</span><br><span class="line"><span class="comment">/*accounts.size()为 1，里面包含的对象的 account_id 是 3，这说明 account 的条件和父对象 role 的条件同时生效*/</span></span><br></pre></td></tr></table></figure></p>
<p>这个特性在 selectOne、count 中同样存在</p>
<p>然后，父对象同样可以参与子对象的 insert、update、updatePersistent，代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">Account newAccount = <span class="keyword">new</span> Account();</span><br><span class="line"></span><br><span class="line"><span class="comment">/*我们新建一个姓名为 iris，角色名称为 "user" 的账号*/</span></span><br><span class="line">newAccount.setName(<span class="string">"iris"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*角色名称为 "user" 的数据的 role_id 是 10，由变量 role1 来加载它*/</span></span><br><span class="line">Role role1 = roleMapper.select(<span class="number">10</span>);</span><br><span class="line">newAccount.setRole(role1);</span><br><span class="line">accountMapper.insert(newAccount);</span><br><span class="line"><span class="comment">/*一个姓名为iris，角色名称为"user"的账号建立完成*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*我们用update方法将iris的角色变为"super_user"*/</span></span><br><span class="line"><span class="comment">/*角色名称为"super_user"的数据的role_id是11，由变量role2来加载了它*/</span></span><br><span class="line">Role role2 = roleMapper.select(<span class="number">11</span>);</span><br><span class="line">newAccount.setRole(role2);</span><br><span class="line">accountMapper.update(newAccount);</span><br><span class="line"><span class="comment">/*现在newAccount.getRole().getId()为11，newAccount.getRole().getRoleName为"super_user"*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/*我们用updatePersistent方法将iris的角色变为null，即与Role对象不再关联*/</span></span><br><span class="line">newAccount.setRole(<span class="keyword">null</span>);</span><br><span class="line">accountMapper.updatePersistent(newAccount);</span><br><span class="line"><span class="comment">/*现在 newAccount.getRole()为 null，在数据库中也不再有关联（注意在这里 update 方法起不到这种效果，因为 update 会忽略 null）*/</span></span><br></pre></td></tr></table></figure></p>
<p>最后，json 中的关联关系可以被故意忽略，例如查询方法的 “properties” 中不写 “role” 的话：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;select id=&quot;selectWithoutRole&quot; resultMap=&quot;result&quot;&gt;</span><br><span class="line">  &#123;&quot;action&quot;:&quot;select#&#123;?&#125;&quot;, &quot;properties&quot;:&#123;&#125;&#125;</span><br><span class="line">&lt;/select&gt;</span><br></pre></td></tr></table></figure></p>
<p>这个方法在执行时就不会自动加载 role 属性。</p>
<h2 id="复杂外键关系"><a href="#复杂外键关系" class="headerlink" title="复杂外键关系"></a><a href="#复杂外键关系">复杂外键关系</a></h2><p>自 <code>0.9.9</code> 开始支持复杂的外键关系，实现方式为在注解 <a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/FieldMapperAnnotation.java" target="_blank" rel="noopener">@FieldMapperAnnotation</a> 中加入 <a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/ForeignAssociation.java" target="_blank" rel="noopener">类型为 @ForeignAssociation[] 的新属性 associationExtra()</a>，例如为实现 ‘a left join b on (a.f_id = b.id and a.name_a = b.name_b and a.version_a &gt;= b.version_b and …)’，您可以使用如下代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"f_id"</span>, jdbcType = JdbcType.Integer, dbAssociationUniqueKey = <span class="string">"id"</span>, associationExtra = &#123;</span><br><span class="line">			<span class="meta">@ForeignAssociation</span>(dbFieldName = <span class="string">"name_a"</span>, dbAssociationFieldName = <span class="string">"name_b"</span>),</span><br><span class="line">			<span class="meta">@ForeignAssociation</span>(dbFieldName = <span class="string">"version_a"</span>, dbAssociationFieldName = <span class="string">"version_b"</span> ,condition=AssociationCondition.GreaterOrEqual) &#125;)</span><br></pre></td></tr></table></figure>
<p>同时自 <code>0.9.9</code> 起在默认左外连接的基础上，增加了右外连接，实现方式为在注解 <a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/FieldMapperAnnotation.java" target="_blank" rel="noopener">@FieldMapperAnnotation</a> 中加入 <a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/statics/AssociationType.java" target="_blank" rel="noopener">新属性 associationType()</a>，例如为实现一个右外连接您可以使用如下代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"f_id"</span>, jdbcType = JdbcType.Integer, dbAssociationUniqueKey = <span class="string">"id"</span>, associationType = AssociationType.RightJoin)</span><br></pre></td></tr></table></figure>
<h2 id="complex-condition"><a href="#complex-condition" class="headerlink" title="complex condition"></a><a href="#complex-condition">complex condition</a></h2><p>之前我们展示的例子中，条件只有“相等”一种，但在实际情况中我们会遇到各种各样的条件：大于、不等于、like、in、is not null 等等。这些情况 flying 也是能够处理的，但首先我们要引入一个“条件对象”的概念。条件对象是实体对象的子类，但它只为查询而存在，它拥有实体对象的全部属性，同时它还有一些专为查询服务的属性。例如下面是 Account 对象的条件对象 AccountCondition 的代码（只需要继承一个 pojo 并实现 Conditionable 接口即可）：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> java.util.Collection;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.ConditionMapperAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.QueryMapperAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.models.Conditionable;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.statics.ConditionType;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AccountCondition</span> <span class="keyword">extends</span> <span class="title">Account</span> <span class="keyword">implements</span> <span class="title">Conditionable</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.Like)</span><br><span class="line">    <span class="comment">/*用作 name 全匹配的值*/</span></span><br><span class="line">    <span class="keyword">private</span> String nameLike;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.HeadLike)</span><br><span class="line">    <span class="comment">/*用作 address 开头匹配的值*/</span></span><br><span class="line">    <span class="keyword">private</span> String addressHeadLike;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.TailLike)</span><br><span class="line">    <span class="comment">/*用作 address 结尾匹配的值*/</span></span><br><span class="line">    <span class="keyword">private</span> String addressTailLike;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.MultiLikeAND)</span><br><span class="line">    <span class="comment">/*用作 address 需要同时匹配的若干个值的集合（类型只能为List）*/</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; addressMultiLikeAND;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.MultiLikeOR)</span><br><span class="line">    <span class="comment">/*用作 address 需要至少匹配之一的若干个值的集合（类型只能为List）*/</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; addressMultiLikeOR;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.In)</span><br><span class="line">    <span class="comment">/*用作 address 可能等于的若干个值的集合（类型只能为List）*/</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; addressIn;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.NotIn)</span><br><span class="line">    <span class="comment">/*用作 address 不可能等于的若干个值的集合（类型只能为List）*/</span></span><br><span class="line">    <span class="keyword">private</span> List&lt;String&gt; addressNotIn;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"address"</span>, conditionType = ConditionType.NullOrNot)</span><br><span class="line">    <span class="comment">/*用作 address 是否为 null 的判断（类型只能为Boolean）*/</span></span><br><span class="line">    <span class="keyword">private</span> Boolean addressIsNull;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/*以下四个方法是实现 Conditionable 接口后必须要定义的方法，我们这里只写出默认实现，在下一节中我们会详细介绍它们*/</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Limitable <span class="title">getLimiter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLimiter</span><span class="params">(Limitable limiter)</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Sortable <span class="title">getSorter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSorter</span><span class="params">(Sortable sorter)</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>以上各种条件并非要全部写出，您可以只写出业务需要的条件（变量名可以是任意的，只要条件标注准确即可）。在 flying 中进行复杂条件查询前需要先按需求写一些条件代码，但请您相信，从长远来看这种做法的回报率是极高的。然后我们可以进行测试：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*查询名称中带有"a"的帐户数量*/</span></span><br><span class="line">AccountCondition condition1 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition1.setNameLike(<span class="string">"a"</span>);</span><br><span class="line"><span class="keyword">int</span> count1 = accountMapper.count(condition1);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址以"bei"开头的帐户的数量*/</span></span><br><span class="line">AccountCondition condition2 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition2.setAddressHeadLike(<span class="string">"bei"</span>);</span><br><span class="line"><span class="keyword">int</span> count2 = accountMapper.count(condition2);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址以"jing"结尾的帐户的数量*/</span></span><br><span class="line">AccountCondition condition3 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition3.setAddressTailLike(<span class="string">"jing"</span>);</span><br><span class="line"><span class="keyword">int</span> count3 = accountMapper.count(condition3);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址同时包含"e"和"i"的账户的数量*/</span></span><br><span class="line">List&lt;String&gt; listAddressMultiLikeAND = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">listAddressMultiLikeAND.add(<span class="string">"e"</span>);</span><br><span class="line">listAddressMultiLikeAND.add(<span class="string">"i"</span>);</span><br><span class="line">AccountCondition condition4 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition4.setAddressMultiLikeAND(listAddressMultiLikeAND);</span><br><span class="line"><span class="keyword">int</span> count4 = accountMapper.count(condition4);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址至少包含"e"或"i"的账户的数量*/</span></span><br><span class="line">List&lt;String&gt; listAddressMultiLikeOR = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">listAddressMultiLikeOR.add(<span class="string">"e"</span>);</span><br><span class="line">listAddressMultiLikeOR.add(<span class="string">"i"</span>);</span><br><span class="line">AccountCondition condition5 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition5.setAddressMultiLikeOR(listAddressMultiLikeOR);</span><br><span class="line"><span class="keyword">int</span> count5 = accountMapper.count(condition5);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址等于"beijing"或"shanghai"中的一个的账户的数量*/</span></span><br><span class="line">List&lt;String&gt; listAddressIn = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">listAddressIn.add(<span class="string">"beijing"</span>);</span><br><span class="line">listAddressIn.add(<span class="string">"shanghai"</span>);</span><br><span class="line">AccountCondition condition6 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition6.setAddressIn(listAddressIn);</span><br><span class="line"><span class="keyword">int</span> count6 = accountMapper.count(condition6);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址不等于"beijing"或"shanghai"的账户的数量*/</span></span><br><span class="line">List&lt;String&gt; listAddressNotIn = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">listAddressNotIn.add(<span class="string">"beijing"</span>);</span><br><span class="line">listAddressNotIn.add(<span class="string">"shanghai"</span>);</span><br><span class="line">AccountCondition condition7 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition7.setAddressNotIn(listAddressNotIn);</span><br><span class="line"><span class="keyword">int</span> count7 = accountMapper.count(condition7);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询地址为null的账户的数量*/</span></span><br><span class="line">AccountCondition condition8 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition8.setAddressIsNull(<span class="keyword">true</span>);</span><br><span class="line"><span class="keyword">int</span> count8 = accountMapper.count(condition8);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*最后我们查询名称中带有"a"且地址为"beijing"的帐户的数量*/</span></span><br><span class="line">AccountCondition conditionX = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">conditionX.setNameLike(<span class="string">"a"</span>);</span><br><span class="line">conditionX.setAddress(<span class="string">"beijing"</span>);</span><br><span class="line"><span class="keyword">int</span> countX = accountMapper.count(conditionX);</span><br><span class="line"><span class="comment">/*这个用例说明条件变量也可以使用 pojo 本身的字段进行查询*/</span></span><br></pre></td></tr></table></figure></p>
<h2 id="limiter-amp-sorter"><a href="#limiter-amp-sorter" class="headerlink" title="limiter &amp; sorter"></a><a href="#limiter-amp-sorter">limiter &amp; sorter</a></h2><p>在之前的 selectAll 查询中我们都是取符合条件的所有值，但在实际业务需求中很少会这样做，更多的情况是我们会有一个数量限制。同时我们还会希望结果集是经过某种条件排序，甚至是经过多种条件排序的，幸运的是 flying 已经为此做好了准备。</p>
<p>一个可限制数量并可排序的查询也是由条件对象来实现的，代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> myPackage;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.annotations.QueryMapperAnnotation;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.models.Conditionable;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.models.Limitable;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.models.Sortable;</span><br><span class="line"><span class="meta">@QueryMapperAnnotation</span>(tableName = <span class="string">"account"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AccountCondition</span> <span class="keyword">extends</span> <span class="title">Account</span> <span class="keyword">implements</span> <span class="title">Conditionable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Limitable limiter;</span><br><span class="line">    <span class="keyword">private</span> Sortable sorter;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Limitable <span class="title">getLimiter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> limiter;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setLimiter</span><span class="params">(Limitable limiter)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.limiter = limiter;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> Sortable <span class="title">getSorter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> sorter;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setSorter</span><span class="params">(Sortable sorter)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.sorter = sorter;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>以上 limiter 和 sorter 变量名并非固定，只要类引入了 Conditionable 接口并实现相关方法，且在相关方法中对应上您定义的 limiter 和 sorter 即可。<br>然后可以采用如下代码进行测试：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> indi.mybatis.flying.models.Conditionable;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.pagination.Order;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.pagination.PageParam;</span><br><span class="line"><span class="keyword">import</span> indi.mybatis.flying.pagination.SortParam;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询 account 表在默认排序下前 10 条数据*/</span></span><br><span class="line">AccountCondition condition1 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line"><span class="comment">/*PageParam 的构造函数中第一个参数为起始页数，第二个参数为每页容量，new PageParam(0,10)即从头开始取 10 条数据*/</span></span><br><span class="line">condition1.setLimiter(<span class="keyword">new</span> PageParam(<span class="number">0</span>, <span class="number">10</span>));</span><br><span class="line">Collection&lt;Account&gt; collection1 = accountMapper.selectAll(codition1);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询 account 表在默认排序下第 8 条数据*/</span></span><br><span class="line">AccountCondition condition2 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line"><span class="comment">/*new PageParam(7,1)即从第 7 条开始取 1 条数据*/</span></span><br><span class="line">condition2.setLimiter(<span class="keyword">new</span> PageParam(<span class="number">7</span>, <span class="number">1</span>));</span><br><span class="line"><span class="comment">/*因为结果只需要一条数据，我们可以使用 selectOne 方法*/</span></span><br><span class="line">Account account2 = accountMapper.selectOne(condition2);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询 account 表在 name 正序排序下的所有数据*/</span></span><br><span class="line">AccountCondition condition3 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line"><span class="comment">/*new Order()的第一个参数是被排序的字段名，第二个参数是正序或倒序*/</span></span><br><span class="line">condition3.setSorter(<span class="keyword">new</span> SortParam(<span class="keyword">new</span> Order(<span class="string">"name"</span>, Conditionable.Sequence.asc)));</span><br><span class="line">Collection&lt;Account&gt; collection3 = accountMapper.selectAll(codition3);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*查询 account 表先在 name 正序排序，然后在 address 倒序排序下的所有数据*/</span></span><br><span class="line">AccountCondition condition4 = <span class="keyword">new</span> AccountCondition();</span><br><span class="line"><span class="comment">/*在new SortParam()中可以接受不定数量的 Order 参数，因此我们先新建一个 name 正序，再新建一个 address 倒序*/</span></span><br><span class="line">condition4.setSorter(<span class="keyword">new</span> SortParam(<span class="keyword">new</span> Order(<span class="string">"name"</span>, Conditionable.Sequence.asc),<span class="keyword">new</span> Order(<span class="string">"address"</span>, Conditionable.Sequence.desc)));</span><br><span class="line">Collection&lt;Account&gt; collection4 = accountMapper.selectAll(codition4);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*最后我们查询在 name 正序排序下的第 11 到 20 条数据*/</span></span><br><span class="line">AccountCondition conditionX = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">conditionX.setSorter(<span class="keyword">new</span> SortParam(<span class="keyword">new</span> Order(<span class="string">"name"</span>, Conditionable.Sequence.asc)));</span><br><span class="line">conditionX.setLimiter(<span class="keyword">new</span> PageParam(<span class="number">1</span>, <span class="number">10</span>));</span><br><span class="line">Collection&lt;Account&gt; collectionX = accountMapper.selectAll(coditionX);</span><br><span class="line"><span class="comment">/*这个用例说明 limiter 和 sorter 是可以组合使用的*/</span></span><br></pre></td></tr></table></figure></p>
<p>因为 limiter 和 sorter 也是以条件对象的方式定义，所以可以和复杂查询一起使用，只要在条件对象中既包含条件标注又包含 Limitable 和 Sortable 类型的变量即可。</p>
<h2 id="分页"><a href="#分页" class="headerlink" title="分页"></a><a href="#分页">分页</a></h2><p>在大多数实际业务需求中，我们的 limiter 和 sorter 都是为分页服务。在 flying 中，我们提供了一种泛型 Page&lt;?&gt; 来封装查询出的数据。使用 Page&lt;?&gt; 的好处是，它除了提供数据内容（pageItems）外还提供了全部数量（totalCount）、最大页数（maxPageNum）、当前页数（pageNo）等信息，这都是数据接收端希望了解的信息。并且这些数量信息是 flying 自动获取的，您只需执行下面这样的代码即可：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> indi.mybatis.flying.pagination.Page;</span><br><span class="line"></span><br><span class="line">AccountCondition condition = <span class="keyword">new</span> AccountCondition();</span><br><span class="line">condition.setLimiter(<span class="keyword">new</span> PageParam(<span class="number">0</span>, <span class="number">10</span>));</span><br><span class="line">Collection&lt;Account&gt; collection = accountMapper.selectAll(condition);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*下面这句代码就将查询结果封装为了 Page&lt;?&gt; 对象*/</span></span><br><span class="line">Page&lt;Account&gt; page = <span class="keyword">new</span> Page&lt;&gt;(collection, condition.getLimiter());</span><br><span class="line"><span class="comment">/*需要注意的是上面的入参 condition.getLimiter() 是不能用其它任意 PageParam 对象代替的，因为在之前执行 selectAll 时已经将一些信息保存到 condition.getLimiter() 中*/</span></span><br></pre></td></tr></table></figure></p>
<p>假设总的数据有 21 条，则 <code>page.getTotalCount()</code> 为 21，<code>pagegetMaxPageNum()</code> 为 3，<code>page.getPageNo()</code> 为 1，<code>page.getPageItems()</code> 为第一到第十条数据的集合。</p>
<h2 id="乐观锁"><a href="#乐观锁" class="headerlink" title="乐观锁"></a><a href="#乐观锁">乐观锁</a></h2><p>乐观锁是实际应用的数据库设计中重要的一环，而 flying 在设计之初就考虑到了这一点，<br>目前 flying 只支持版本号型乐观锁。在 flying 中使用乐观锁的方法如下：<br>在数据结构中增加一个表示乐观锁的 Integer 型字段 opLock 即可：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"opLock"</span>, jdbcType = JdbcType.INTEGER, opLockType = OpLockType.Version)</span><br><span class="line"><span class="keyword">private</span> Integer opLock;</span><br><span class="line"><span class="comment">/*乐观锁可以增加 getter 方法，不建议增加 setter 方法*/</span></span><br></pre></td></tr></table></figure></p>
<p>以上实际上是给 <code>@FieldMapperAnnotation</code> 中的 <code>opLockType</code> 上赋予了 <code>OpLockType.Version</code>，这样 flying 就会明白这是一个起乐观锁作用的字段。当含有乐观锁的表 account 更新时，实际 sql 会变为：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">update account ... and opLock = opLock + 1 where id = '$&#123;id&#125;' and opLock = '$&#123;opLock&#125;'</span><br></pre></td></tr></table></figure></p>
<p>（上面 … 中的内容是给其它的字段赋值）</p>
<p>每次更新时都会加入 opLock 的判断，并且更新数据时 opLock 自增 1 ，这样就可以保证多个线程对同一个 account 执行 update 或 updatePersistent 时只有一个能执行成功，即达到了我们需要的锁效果。</p>
<p>当含有乐观锁的表 account 删除时，实际 sql 会变为：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">delete from account where id = '$&#123;id&#125;' and opLock = '$&#123;opLock&#125;'</span><br></pre></td></tr></table></figure></p>
<p>即只有 opLock 和 id 都符合时才能被删除，这里乐观锁起到了保护数据的作用。</p>
<p>在实际应用中，可以借助 update、updatePersistent、delete 方法的返回值来判断是否变动了数据（一般来说返回 0 表示没变动，1 表示有变动），继而判断锁是否有效，是否合法（符合业务逻辑），最后决定整个事务是提交还是回滚。</p>
<p>最后我们再来谈谈为什么不建议给乐观锁字段加上 setter 方法。首先在代码中直接修改一个 pojo 的乐观锁值是很危险的事情，它会导致事务逻辑的不可靠；其次乐观锁不参与 select、selectAll、selectOne 方法，即便给它赋值在查询时也不会出现；最后乐观锁不参与 insert 方法，无论给它赋什么值在新增数据中此字段的值都是零，即乐观锁总是从零开始增长。</p>
<h2 id="或逻辑查询"><a href="#或逻辑查询" class="headerlink" title="或逻辑查询"></a><a href="#或逻辑查询">或逻辑查询</a></h2><p>为实现此特性 flying 使用了 Or 标签类（<a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/annotations/Or.java" target="_blank" rel="noopener">代码见此</a>），这个标签的内容是ConditionMapperAnnotation标签的数组，所以在查询条件类中可以有如下标签代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Or</span>(&#123;</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.HeadLike),</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"age"</span>, conditionType = ConditionType.Equal),</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.HeadLike)</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
<p>（上面是实现 name like ‘XXX%’ or age = ‘YYY’ or name like ‘ZZZ%’ 查询的条件）</p>
<p>同时为了赋值方便，我们采用Object数组的不定参数形式作为变量类型，于是整个代码变成了：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Or</span>(&#123;</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.HeadLike),</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"age"</span>, conditionType = ConditionType.Equal),</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.HeadLike)</span><br><span class="line">&#125;)</span><br><span class="line"><span class="keyword">private</span> Object[] condition1;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Object[] getCondition1 () &#123;</span><br><span class="line">	<span class="keyword">return</span> condition1;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setCondition1</span> <span class="params">(Object... condition1)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">this</span>. condition1 = condition1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>如果我们描述 “name like ‘张%’ or age = 27 or name like ‘李%’ “，代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">personCondition.setCondition1(<span class="string">"张"</span>, <span class="number">27</span>, <span class="string">"李"</span>);</span><br><span class="line"><span class="comment">/* 注意参数顺序和 condition1 上 @ConditionMapperOrAnnotation 的内部顺序一致 */</span></span><br></pre></td></tr></table></figure></p>
<p>您之前掌握的<a href="https://gitee.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/statics/ConditionType.java" target="_blank" rel="noopener">绝大部分 ConditionMapperAnnotation</a> 都可以写在 <code>@Or</code> 中，只有本身就是集合型的条件类型例外，以下列出不能进入 <code>@Or</code> 的类型：</p>
<p><code>MultiLikeAND</code>、<code>MultiLikeOR</code>、 <code>In</code>、<code>NotIn</code></p>
<p>除此之外的查询条件均可以参与或查询。</p>
<h3 id="外键或逻辑查询"><a href="#外键或逻辑查询" class="headerlink" title="外键或逻辑查询"></a><a href="#外键或逻辑查询">外键或逻辑查询</a></h3><p>flying 在同库跨表查询时也可以做不同表上条件的或逻辑查询，比如我们要实现 person.name = ‘XXX’ or role.name = ‘YYY’ 查询，其中 role 是 person 业务上的父对象。我们可以在 role 的条件类中加入如下变量：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Or</span>(&#123;</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.Equal),</span><br><span class="line">  <span class="meta">@ConditionMapperAnnotation</span>(dbFieldName = <span class="string">"name"</span>, conditionType = ConditionType.Equal, subTarget = mypackage.Person.class) &#125;)</span><br><span class="line"><span class="keyword">private</span> Object[] roleNameEqualsOrPersonNameEquals;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> Object[] getRoleNameEqualsOrPersonNameEquals () &#123;</span><br><span class="line">	<span class="keyword">return</span> roleNameEqualsOrPersonNameEquals;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setRoleNameEqualsOrPersonNameEquals</span> <span class="params">(Object... roleNameEqualsOrPersonNameEquals)</span> </span>&#123;</span><br><span class="line">	<span class="keyword">this</span>. roleNameEqualsOrPersonNameEquals = roleNameEqualsOrPersonNameEquals;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>上面的代码中第一行 <code>ConditionMapperAnnotation</code> 指的是 role 表，第二行指的是 person 表，因为是由 role 指向 person，所以第二行出现了 <code>subTarget</code> 参数用来引导路径，它的值就是业务上子对象的类路径。</p>
<p>值得注意的是，外键或逻辑查询中，跨表的 <code>@Or</code> 条件永远要写在业务上的父对象里，这是考虑到从子对象上寻找父对象并非唯一（例如多重外键情况，一个 person 有多个 role 型父对象，分别表示主要角色和次要角色等），然而从父对象上寻找子对象永远是唯一的。</p>
<p>如果您要查询用户名为“张三”或角色名为“wfadmin”的用户时，您只需这样做：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">RoleConditon rc = <span class="keyword">new</span> RoleCondition();</span><br><span class="line">rc.setRoleNameEqualsOrPersonNameEquals(<span class="string">"wfadmin"</span>,<span class="string">"张三"</span>);</span><br><span class="line">Person p = <span class="keyword">new</span> Person();</span><br><span class="line">p.setRole(rc);</span><br><span class="line">Person&lt;Collection&gt; persons = personMapper.selectAll(p);</span><br></pre></td></tr></table></figure></p>
<p>无论 role 是 person 业务上的直接父对象还是间接父对象都可以这样查询。</p>
<h2 id="customTypeHandler"><a href="#customTypeHandler" class="headerlink" title="customTypeHandler"></a><a href="#customTypeHandler">customTypeHandler</a></h2><p>自 <code>0.9.4</code> 起 <code>@FieldMapperAnnotation</code> 和 <code>@ConditionMapperAnnotation</code> 增加了 <code>customTypeHandler</code> 属性，使您可以用自定义的 TypeHandler 来处理变量映射，因为  <code>customTypeHandler</code> 具有最高优先级。</p>
<h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a><a href="#其它">其它</a></h2><h3 id="ignore-tag"><a href="#ignore-tag" class="headerlink" title="ignore tag"></a><a href="#ignore-tag">ignore tag</a></h3><p>有时候，我们希望在查询中忽略某个字段的值，但在作为查询条件和更新时要用到这个字段。一个典型的场景是 password 字段，出于安全考虑我们不想在 select 方法返回的结果中看到它的值，但我们需要在查询条件（如判断登录）和更新（如修改密码）时使用到它，这时我们可以在 Account.java 中加入以下代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"password"</span>, jdbcType = JdbcType.VARCHAR, ignoreTag = &#123; <span class="string">"noPassword"</span> &#125;)</span><br><span class="line"><span class="keyword">private</span> String password;</span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>这样我们将 <code>password</code> 这个字段加上了一个忽略标记 <code>noPassword</code>，然后在查询 account 表时相关 flying-json 属性 <code>&quot;ignore&quot;</code> 加上 <code>noPassword</code> 就不会再查找 password 字段，但作为查询条件和更新数据时 password 字段都可以参与进来，如下所示：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectOne"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">    &#123;"action":"selectAll", "ignore":"noPassword"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>然后，可通过代码验证 <code>password</code> 属性已被忽略<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*查找 name 为 "user" 且 password 为 "123456" 的一个账户*/</span></span><br><span class="line">Account condition = <span class="keyword">new</span> Account();</span><br><span class="line">condition.setName(<span class="string">"user"</span>);</span><br><span class="line">condition.setPassword(<span class="string">"123456"</span>);</span><br><span class="line">Account account = accountMapper.selectOne(condition);</span><br><span class="line"><span class="comment">/*用以上方式是可以查出 passeord 为 "123456" 的账户的，然而结果中 account.getPassword()为 null*/</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 但是仍然可以更新 password 的值 */</span></span><br><span class="line">account.setPassword(<span class="string">"654321"</span>);</span><br><span class="line">accountMapper.update(account);</span><br><span class="line"><span class="comment">/*现在 account 对应的数据库中数据的 password 字段值变为 "654321"*/</span></span><br></pre></td></tr></table></figure></p>
<p>另一种场景是查询对象中有一个长度很大的属性，例如我们在数据库中有一个类型为 varchar 长度为 3000 的属性 <code>detail</code>，为性能考虑，在不需要查看 <code>detail</code> 详情的情况下我们不想将其 select 出来，而忽略标记就可以做到这一点。我们在 account.java 中增加如下代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"detail"</span>, jdbcType = JdbcType.VARCHAR, ignoreTag = &#123; <span class="string">"noDetail"</span> &#125;)</span><br><span class="line"><span class="keyword">private</span> String detail;</span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>此时用 flying-json 为 <code>{&quot;action&quot;:&quot;select#{?}&quot;, &quot;ignore&quot;:&quot;noDetail&quot;}</code> 的方法就不会查出 <code>detail</code> 字段。</p>
<p>如果我们想既不查询 <code>detail</code> 又不查询 <code>password</code>，可在 <code>password</code> 的注解上使用多个忽略标记，就像下面这样：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"password"</span>, jdbcType = JdbcType.VARCHAR, ignoreTag = &#123; <span class="string">"noPassword"</span>, <span class="string">"noDetail"</span> &#125;)</span><br><span class="line"><span class="keyword">private</span> String password;</span><br></pre></td></tr></table></figure></p>
<p>这时 flying-json <code>{&quot;action&quot;:&quot;select#{?}&quot;, &quot;ignore&quot;:&quot;noDetail&quot;}</code> 就既忽略 <code>detail</code> 又忽略 <code>password</code>。<br>由此可见，在实体类中一个属性可配置多个忽略标记，其中一个被激活这个属性就不会参与查询；但是 flying-json 每次只能激活一个忽略标记，所以如果您有多样化的忽略需求，您需要在实体类中仔细配置以满足需要。</p>
<p>最后，flyin-json 中的忽略标记没有传递性，只对当前查询对象有效而对自动查询的父对象无效。例如对 <code>Account</code> 对象的忽略标记对自动查询的父对象 <code>Role</code> 无效，哪怕 <code>Role</code> 中有 <code>ignoreTag</code> 等于 Account 查询 json 中 <code>&quot;ignore&quot;</code> 的属性也会查询出来。如果您需要激活自动查询的父对象中的忽略标记，您需要调整 <code>&quot;properties&quot;</code> 中的属性中的 <code>&quot;id&quot;</code>，让其指向一个激活了忽略标记的查询，例如：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;"action":"select#&#123;?&#125;", "properties":&#123;</span><br><span class="line">			"role":&#123;"id":"myPackage.RoleMapper.selectWithIgnore", "prefix":"role__"&#125;,</span><br><span class="line">		&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>如果您既需要带有忽略父对象参数的方法，又需要不忽略的方法，那您需要定义不同的方法。</p>
<p>忽略标记在 insert、update、updatePersist 中同样会生效，具体作用是激活后新增、修改时忽略此字段，某些情况下您会发现这种需要。另外，如果此字段受到 JPA 标签 <code>@Column</code> 修饰并且 <code>insertable = false</code> 或 <code>updateable = false</code>，则不论此字段上的 ignoreTag 为何，在任何情况下此字段都被认为是新增忽略或修改忽略。</p>
<p>对于关联对象型属性来说，在 flying-json 的 <code>&quot;properties&quot;</code> 中忽略这个属性和对这个属性使用 ignore 标签效果是一样的。</p>
<h3 id="whiteList-tag"><a href="#whiteList-tag" class="headerlink" title="whiteList tag"></a><a href="#whiteList-tag">whiteList tag</a></h3><p>如果说 ignore 机制是一种黑名单机制，那么 whiteList 机制就是白名单机制。例如有时候我们想精确控制查询结果中的字段，使用 whiteList 机制就比 ignore 机制更好。例如我们的 Account 中有 <code>password</code> 和 ‘salt’（加密盐值），两者都属于保密属性，在代码中就可以如下写：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"password"</span>, jdbcType = JdbcType.VARCHAR, whiteListTag = &#123; <span class="string">"secret"</span> &#125;)</span><br><span class="line"><span class="keyword">private</span> String password;</span><br><span class="line"></span><br><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"salt"</span>, jdbcType = JdbcType.VARCHAR, whiteListTag = &#123; <span class="string">"secret"</span> &#125;)</span><br><span class="line"><span class="keyword">private</span> String salt;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>如果我们查询 account 表的 flying-json 中不含有 <code>&quot;whiteList&quot;</code> 属性，whiteListTag 不会起作用。如果 flying-json 属性 <code>&quot;whiteList&quot;</code> 有值就会<b>只查询相应白名单上的字段，其它字段都忽略</b>，例如：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">select</span> <span class="attr">id</span>=<span class="string">"selectOneOnlySecret"</span> <span class="attr">resultMap</span>=<span class="string">"result"</span>&gt;</span></span><br><span class="line">    &#123;"action":"selectOne", "whiteList":"secret"&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">select</span>&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>然后，可通过代码验证 <code>password</code>、<code>salt</code> 之外的属性已被忽略<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*查找 name 为 "user" 且 password 为 "123456" 的一个账户*/</span></span><br><span class="line">Account condition = <span class="keyword">new</span> Account();</span><br><span class="line">condition.setName(<span class="string">"user"</span>);</span><br><span class="line">condition.setPassword(<span class="string">"123456"</span>);</span><br><span class="line">Account account = accountMapper.selectOneOnlySecret(condition);</span><br><span class="line"><span class="comment">/*用以上方式是可以查出 passeord 为 "123456" 的账户的，然而结果中只有 account.getPassword() 和 account.getSalt() 不为 null*/</span></span><br></pre></td></tr></table></figure></p>
<p>和 ignore 机制一样，您可以在代码中给属性配置多个白名单标记，但是 flying-json 每次只能激活一个白名单标记。</p>
<p>如果您习惯了在每个查询 flying-json 上都加上 <code>&quot;whiteList&quot;</code>，您会发现这种机制更适合保护敏感字段。</p>
<p>whiteList 机制和 ignore 机制可以同时生效，如同您的直觉一样，先处理白名单再处理黑名单。</p>
<h3 id="复数外键"><a href="#复数外键" class="headerlink" title="复数外键"></a><a href="#复数外键">复数外键</a></h3><p>有时候一个数据实体会有多个多对一关系指向另一个数据实体，例如考虑下面的情况：我们假设每个账户都有一个兼职角色，这样 account 表中就需要另一个字段 fk_second_role_id，而这个字段也是指向 role 表。为了满足这个需要，首先我们要在 account.xml 的 resultMap元素中，加入以下内容：<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">association</span> <span class="attr">property</span>=<span class="string">"secondRole"</span> <span class="attr">resultMap</span>=<span class="string">"myPackage.RoleMapper.result"</span> <span class="attr">columnPrefix</span>=<span class="string">"secondRole__"</span> /&gt;</span></span><br></pre></td></tr></table></figure></p>
<p>在相关的 flying-json 中的 <code>&quot;properties&quot;</code> 中加入<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&quot;secondRole&quot;:&#123;&quot;id&quot;:&quot;myPackage.RoleMapper.select&quot;, &quot;prefix&quot;:&quot;secondRole__&quot;&#125;</span><br></pre></td></tr></table></figure></p>
<p>然后在 Account.java 中还需要加入以下代码：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FieldMapperAnnotation</span>(dbFieldName = <span class="string">"fk_second_role_id"</span>, jdbcType = JdbcType.INTEGER, dbAssociationUniqueKey = <span class="string">"role_id"</span>)</span><br><span class="line"><span class="keyword">private</span> Role secondRole;   </span><br><span class="line"><span class="comment">/*相关的getter和setter方法请自行补充*/</span></span><br></pre></td></tr></table></figure></p>
<p>如此一来表 account 和表 role 就构成了复数外键关系。flying 支持复数外键，您可以像操作普通外键一样操作它，代码如下：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*查询角色名称为 "user",同时兼职角色名称为 "super_user" 的账户*/</span></span><br><span class="line">Account condition = <span class="keyword">new</span> Account();</span><br><span class="line">Role role = <span class="keyword">new</span> Role(), secondRole = <span class="keyword">new</span> Role();</span><br><span class="line">role.setName(<span class="string">"user"</span>);</span><br><span class="line">condition.setRole(role);</span><br><span class="line">secondRole.setName(<span class="string">"super_user"</span>);</span><br><span class="line">condition.setSecondRole(secondRole);</span><br><span class="line">Collection&lt;Account&gt; accounts = accountMapper.selectAll(condition);</span><br></pre></td></tr></table></figure></p>
<p>可见，复数外键的增删改查等操作与普通外键是类似的，只需要注意虽然 secondRole 的类型为Role，但它的 getter、setter 是 getSecondRole()、setSecondRole()，而不是 getRole()、setRole()即可。</p>
<h3 id="兼容-JPA-标签"><a href="#兼容-JPA-标签" class="headerlink" title="兼容 JPA 标签"></a><a href="#兼容-JPA-标签">兼容 JPA 标签</a></h3><p>flying 对部分常用的 JPA 标签进行了兼容，具体内容为：</p>
<ul>
<li><code>@Id</code> 变量标签，表示此变量对应主键</li>
<li><code>@Column</code> 变量标签，描述字段定义，对 flying 有效属性：<code>name</code>（默认为该变量名）、<code>insertable</code>(若为 false 此字段在新增时永远被忽略)、<code>updateable</code>(若为 false 此字段在修改时永远被忽略)、<code>columnDefinition</code>（默认为该变量类型名，若指定则按值来推导类型，对应关系<a href="https://github.com/limeng32/mybatis.flying/blob/master/src/main/java/indi/mybatis/flying/utils/JdbcTypeEnum.java" target="_blank" rel="noopener">见此</a>）</li>
<li><code>@Table</code> 类标签，描述表定义，对 flying 有效属性：<code>name</code>（默认为类名）</li>
</ul>
<p>和其它 JPA 实现不同的是，以上变量标签只在变量上有效，在 getter 方法上无效。在以上标签和 <code>FieldMapperAnnotation</code>、<code>TableMapperAnnotation</code> 同时出现时，优先级为（高优先级会覆盖低优先级的相关属性）：</p>
<ul>
<li><code>@Id</code> 最高。</li>
<li><code>@FieldMapperAnnotation</code> 和 <code>TableMapperAnnotation</code> 其次。</li>
<li><code>@Column</code> 和 <code>@Table</code> 再次。</li>
</ul>
<h2 id="附录"><a href="#附录" class="headerlink" title="附录"></a><a href="#附录">附录</a></h2><p><a id="FAQ"></a></p>
<h3 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a><a href="#常见问题">常见问题</a></h3><p><a id="why-no-sql"></a><br>1、为何<i>pojo_mapper</i>.xml 中没有 sql 语句细节？<br>A：flying 的 sql 语句是动态生成的，只要您指定了正确的字段名，就绝对不会出现 sql 书写上的问题。并且 flying 采用了缓存机制，您无需担心动态生成 sql 的效率问题。</p>
<p><a id="AccountTableCreater"></a></p>
<h3 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a><a href="#代码示例">代码示例</a></h3><p>为了您更方便的使用 flying 进行开发，我们提供了一个<a href="https://gitee.com/limeng32/flying-demo-use-springboot" target="_blank" rel="noopener">覆盖了本文大部分功能的代码示例</a>。</p>
<h3 id="account-表建表语句"><a href="#account-表建表语句" class="headerlink" title="account 表建表语句"></a><a href="#account-表建表语句">account 表建表语句</a></h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">account</span> (</span><br><span class="line">  account_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT,</span><br><span class="line">  <span class="keyword">name</span> <span class="built_in">varchar</span>(<span class="number">20</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  address <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  fk_role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  fk_second_role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (account_id)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p><a id="RoleTableCreater"></a></p>
<h3 id="role-表建表语句"><a href="#role-表建表语句" class="headerlink" title="role 表建表语句"></a><a href="#role-表建表语句">role 表建表语句</a></h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="keyword">role</span> (</span><br><span class="line">  role_id <span class="built_in">int</span>(<span class="number">11</span>) <span class="keyword">NOT</span> <span class="literal">NULL</span> AUTO_INCREMENT,</span><br><span class="line">  role_name <span class="built_in">varchar</span>(<span class="number">30</span>) <span class="keyword">DEFAULT</span> <span class="literal">NULL</span>,</span><br><span class="line">  PRIMARY <span class="keyword">KEY</span> (role_id)</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
  
	</div>
		<footer class="article-footer clearfix">
<div class="article-catetags">

<div class="article-categories">
  <span></span>
  <a class="article-category-link" href="/categories/blog/">blog</a>
</div>


</div>



	<div class="article-share" id="share">
	
	  <div data-url="http://flying-doc.limeng32.com/2017/05/01/2017-05-01-使用flying解决pojo自动映射问题/" data-title="使用 flying 解决 pojo 自动映射问题 | my bat is flying in Aurora" data-tsina="" class="share clearfix">
	  </div>
	
	</div>


</footer>

   	       
	</article>
	
<nav class="article-nav clearfix">
 
 <div class="prev" >
 <a href="/2017/06/01/2017-06-01-flying 是什么/" title="flying 是什么">
  <strong>上一篇：</strong><br/>
  <span>
  flying 是什么</span>
</a>
</div>


<div class="next">
<a href="/2017/04/15/2017-04-15-flying 最新版本新增特性/"  title="flying 最新版本新增特性">
 <strong>下一篇：</strong><br/> 
 <span>flying 最新版本新增特性
</span>
</a>
</div>

</nav>

	



</div>  
      <div class="openaside"><a class="navbutton" href="#" title="显示侧边栏"></a></div>

  <div id="toc" class="toc-aside">
  <strong class="toc-title">文章目录</strong>
 
 <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#Hello-World"><span class="toc-text">Hello World</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#flying-json-描述"><span class="toc-text">flying-json 描述</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#insert-amp-delete"><span class="toc-text">insert &amp; delete</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#update-amp-updatePersistent"><span class="toc-text">update &amp; updatePersistent</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#selectAll-amp-selectOne-amp-count"><span class="toc-text">selectAll &amp; selectOne &amp; count</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#foreign-key"><span class="toc-text">foreign key</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#复杂外键关系"><span class="toc-text">复杂外键关系</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#complex-condition"><span class="toc-text">complex condition</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#limiter-amp-sorter"><span class="toc-text">limiter &amp; sorter</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#分页"><span class="toc-text">分页</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#乐观锁"><span class="toc-text">乐观锁</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#或逻辑查询"><span class="toc-text">或逻辑查询</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#外键或逻辑查询"><span class="toc-text">外键或逻辑查询</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#customTypeHandler"><span class="toc-text">customTypeHandler</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#其它"><span class="toc-text">其它</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#ignore-tag"><span class="toc-text">ignore tag</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#whiteList-tag"><span class="toc-text">whiteList tag</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#复数外键"><span class="toc-text">复数外键</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#兼容-JPA-标签"><span class="toc-text">兼容 JPA 标签</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#附录"><span class="toc-text">附录</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#常见问题"><span class="toc-text">常见问题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#代码示例"><span class="toc-text">代码示例</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#account-表建表语句"><span class="toc-text">account 表建表语句</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#role-表建表语句"><span class="toc-text">role 表建表语句</span></a></li></ol></li></ol>
 
  </div>

<div id="asidepart">
<div class="closeaside"><a class="closebutton" href="#" title="隐藏侧边栏"></a></div>
<aside class="clearfix">

  <div class="linkslist">
  <p class="asidetitle">友情链接</p>
    <ul>
        
          <li>
            
            	<a href="https://my.oschina.net/u/2280950/blog/1580056" target="_blank" title="关于在网络应用中使用双向相关模型的研究">flying-初雪 理论基础一</a>
            
          </li>
        
          <li>
            
            	<a href="https://my.oschina.net/u/2280950/blog/1594694" target="_blank" title="或逻辑的需求的实现方案">flying-初雪 理论基础二</a>
            
          </li>
        
          <li>
            
            	<a href="http://www.cnblogs.com/dasn/articles/5208022.html" target="_blank" title="为什么我们在初雪版中引入自定义主键机制">flying-初雪 新增自定义主键生成器特性</a>
            
          </li>
        
    </ul>
</div>

</aside>
</div>
    </div>
    <footer><div id="footer" >
	
	<div class="line">
		<span></span>
		<div class="author"></div>
	</div>
	
	
	<section class="info">
		<p> God is in his heaven, all&#39;s right with the code. </p>
	</section>
	 
	<div class="social-font" class="clearfix">
		
		
		<a href="https://gitee.com/limeng32/mybatis.flying" target="_blank" class="icon-gitee" title="gitee"></a>
		
		
		<a href="https://github.com/limeng32/mybatis.flying" target="_blank" class="icon-github" title="github"></a>
		
		
		
		
		
		
		
		<a href="http://www.zhihu.com/people/li-meng-48" target="_blank" class="icon-zhihu" title="知乎"></a>
		
		
		
	</div>
			
		

	<p class="copyright">
		written by <a href="/about" target="_blank" title="limeng32">limeng32</a>
		© 2021
	</p>
</div>
</footer>
    <script src="/js/jquery-2.0.3.min.js"></script>
<script src="/js/jquery.imagesloaded.min.js"></script>
<script src="/js/gallery.js"></script>
<script src="/js/jquery.qrcode-0.12.0.min.js"></script>

<script type="text/javascript">
$(document).ready(function(){ 
  $('.navbar').click(function(){
    $('header nav').toggleClass('shownav');
  });
  var myWidth = 0;
  function getSize(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
  };
  var m = $('#main'),
      a = $('#asidepart'),
      c = $('.closeaside'),
      o = $('.openaside');
  c.click(function(){
    a.addClass('fadeOut').css('display', 'none');
    o.css('display', 'block').addClass('fadeIn');
    m.addClass('moveMain');
  });
  o.click(function(){
    o.css('display', 'none').removeClass('beforeFadeIn');
    a.css('display', 'block').removeClass('fadeOut').addClass('fadeIn');      
    m.removeClass('moveMain');
  });
  $(window).scroll(function(){
    o.css("top",Math.max(80,260-$(this).scrollTop()));
  });
  
  $(window).resize(function(){
    getSize(); 
    if (myWidth >= 1024) {
      $('header nav').removeClass('shownav');
    }else{
      m.removeClass('moveMain');
      a.css('display', 'block').removeClass('fadeOut');
      o.css('display', 'none');
      
      $('#toc.toc-aside').css('display', 'none');
        
    }
  });
});
</script>

<script type="text/javascript">
$(document).ready(function(){ 
  var ai = $('.article-content>iframe'),
      ae = $('.article-content>embed'),
      t  = $('#toc'),
      ta = $('#toc.toc-aside'),
      o  = $('.openaside'),
      c  = $('.closeaside');
  if(ai.length>0){
    ai.wrap('<div class="video-container" />');
  };
  if(ae.length>0){
   ae.wrap('<div class="video-container" />');
  };
  c.click(function(){
    ta.css('display', 'block').addClass('fadeIn');
  });
  o.click(function(){
    ta.css('display', 'none');
  });
  $(window).scroll(function(){
    ta.css("top",Math.max(140,320-$(this).scrollTop()));
  });
});
</script>


<script type="text/javascript">
$(document).ready(function(){ 
  var $this = $('.share'),
      url = $this.attr('data-url'),
      encodedUrl = encodeURIComponent(url),
      title = $this.attr('data-title'),
      tsina = $this.attr('data-tsina'),
      description = $this.attr('description');
  var html = [
  '<div class="hoverqrcode clearfix"></div>',
  '<a class="overlay" id="qrcode"></a>',
  '<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="article-share-facebook" target="_blank" title="Facebook"></a>',
  '<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="article-share-twitter" target="_blank" title="Twitter"></a>',
  '<a href="#qrcode" class="article-share-qrcode" title="微信"></a>',
  '<a href="http://widget.renren.com/dialog/share?resourceUrl=' + encodedUrl + '&srcUrl=' + encodedUrl + '&title=' + title +'" class="article-share-renren" target="_blank" title="人人"></a>',
  '<a href="http://service.weibo.com/share/share.php?title='+title+'&url='+encodedUrl +'&ralateUid='+ tsina +'&searchPic=true&style=number' +'" class="article-share-weibo" target="_blank" title="微博"></a>',
  '<span title="Share to"></span>'
  ].join('');
  $this.append(html);

  $('.hoverqrcode').hide();

  var myWidth = 0;
  function updatehoverqrcode(){
    if( typeof( window.innerWidth ) == 'number' ) {
      myWidth = window.innerWidth;
    } else if( document.documentElement && document.documentElement.clientWidth) {
      myWidth = document.documentElement.clientWidth;
    };
    var qrsize = myWidth > 1024 ? 200:100;
    var options = {render: 'image', size: qrsize, fill: '#2ca6cb', text: url, radius: 0.5, quiet: 1};
    var p = $('.article-share-qrcode').position();
    $('.hoverqrcode').empty().css('width', qrsize).css('height', qrsize)
                          .css('left', p.left-qrsize/2+20).css('top', p.top-qrsize-10)
                          .qrcode(options);
  };
  $(window).resize(function(){
    $('.hoverqrcode').hide();
  });
  $('.article-share-qrcode').click(function(){
    updatehoverqrcode();
    $('.hoverqrcode').toggle();
  });
  $('.article-share-qrcode').hover(function(){}, function(){
      $('.hoverqrcode').hide();
  });
});   
</script>











<link rel="stylesheet" href="/fancybox/jquery.fancybox.css" media="screen" type="text/css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script type="text/javascript">
$(document).ready(function(){ 
  $('.article-content').each(function(i){
    $(this).find('img').each(function(){
      if ($(this).parent().hasClass('fancybox')) return;
      var alt = this.alt;
      if (alt) $(this).after('<span class="caption">' + alt + '</span>');
      $(this).wrap('<a href="' + this.src + '" title="' + alt + '" class="fancybox"></a>');
    });
    $(this).find('.fancybox').each(function(){
      $(this).attr('rel', 'article' + i);
    });
  });
  if($.fancybox){
    $('.fancybox').fancybox();
  }
}); 
</script>



<!-- Analytics Begin -->





<!-- Analytics End -->

<!-- Totop Begin -->

	<div id="totop">
	<a title="返回顶部"><img src="/img/scrollup.png"/></a>
	</div>
	<script src="/js/totop.js"></script>

<!-- Totop End -->

<!-- MathJax Begin -->
<!-- mathjax config similar to math.stackexchange -->


<!-- MathJax End -->

<!-- Tiny_search Begin -->

<!-- Tiny_search End -->

  </body>
</html>
